# 与 knowshare 相关的视图

from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
# 导入MultiPartParser-文件附件支持；FormParser-表单支持
from rest_framework.parsers import MultiPartParser, FormParser
# default_storage: Django 提供的默认文件存储后端 默认情况下，它会将文件保存到 MEDIA_ROOT 设置指定的目录中
from django.core.files.storage import default_storage
# 模型
from sa.models import User, ProductType, KnowledgeDomain, Article
# 序列化器
from sa.serializers.KnowledgeShareSerializer import ArticleSerializer
# 导入分页器类 project.py 视图
from sa.views.project import LargeResultsSetPagination

import logging

# 日志记录对象
logger = logging.getLogger('django')


# 涉及产品 options 列表
class ProductOptionsAPIView(APIView):

    def get(self, request):
        product_choices = list(ProductType.objects.all().values('id', 'name'))
        # choices = [{'id': None, 'name': '--请选择--'}] + product_choices
        # print(choices)
        # print(product_choices)
        return Response(data=product_choices)


# 涉及知识 options
class KnowledgeOptionsAPIView(APIView):
    def get(self, request):
        knowledge_choices = list(KnowledgeDomain.objects.all().values('id', 'name'))
        # choices = [{'id': None, 'name': '--请选择--'}] + knowledge_choices
        # print('knowledge_choices:', knowledge_choices)
        return Response(data=knowledge_choices)


# Article视图
class ArticleAPIView(GenericAPIView):
    # 指定序列化器类
    serializer_class = ArticleSerializer
    # 指定查询集
    # select_related 优化“单值”关系的查询，例如外键（`ForeignKey`）和一对一关系（`OneToOneField`）
    # prefetch_related 用于优化“多值”关系的查询，例如多对多关系（`ManyToManyField`）和反向的外键关系
    queryset = Article.objects.all().select_related(
        'author'
    ).prefetch_related(
        'category', 'tags', 'attachments'
    ).order_by('-id')
    permission_classes = [IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]  # 同时支持文件和表单数据
    # 分页器
    pagination_class = LargeResultsSetPagination
    """
        DRF 分页响应格式：
        {
            "count": 100,
            "next": "http://...?page=3",
            "previous": "http://...?page=1",
            "results": [{}, {}, {}...]
        }
    """

    def get(self, request):
        """ 所有文章 """
        # print(request.query_params)
        search_params = {}
        title = request.query_params.get('title')
        if title:
            search_params['title__icontains'] = title
        author = request.query_params.get('author')
        if author:
            search_params['author__cn_name__icontains'] = author
        # 多选字段处理, 使用getlist
        # 注意：
        # 在使用 axios 发送数组参数时，默认会将数组序列化为带[]后缀的参数名（例如category[]=1&category[]=6），
        # 这是 axios 的标准行为，而非错误。这种序列化方式符合 PHP 等后端语言解析数组参数的默认格式
        categories = request.query_params.getlist('category[]')
        if categories:
            search_params['category__id__in'] = categories
        tags = request.query_params.getlist('tags[]')
        if tags:
            search_params['tags__id__in'] = tags
        # print('得到的查询集：', search_params)

        articles = self.get_queryset().filter(**search_params)
        """ 开始分页处理:
        1.使用 paginate_queryset 方法对查询集进行分页切割，返回当前页的数据
        2.使用 get_paginated_response 将切割后的数据包装成标准的分页响应格式，通常包括：
            当前页的数据（results）
            分页元数据（如 count、next、previous 等）  
        """
        page = self.paginate_queryset(articles)  # 分页切割后得到的当前页的查询集（QuerySet）
        if page is not None:
            # 对当页数据做序列化
            article_serializer = self.get_serializer(instance=page, many=True)
            # get_paginated_response方法 对 序列化后的数据 处理成 分页后的的数据格式
            # 标准分页格式 {count, next, previous, results}  前端可获取分页状态
            # data_page = self.get_paginated_response(data=article_serializer.data)
            return self.get_paginated_response(data=article_serializer.data)
        else:  # 如果未分页（page is None），直接序列化全部数据并返回
            article_serializer = self.get_serializer(instance=articles, many=True)
            return Response(data=article_serializer.data)

    def post(self, request):
        """ 新增文章 """
        current_user = request.user
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # print(serializer.validated_data)
        serializer.save()
        # print(serializer.data)
        logger.info(f"{current_user} 新增了文章")
        return Response(data=serializer.data, status=status.HTTP_201_CREATED)


class ArticleDetailAPIView(GenericAPIView):
    serializer_class = ArticleSerializer
    queryset = Article.objects.all().select_related(
        'author'
    ).prefetch_related(
        'category', 'tags', 'attachments'
    ).order_by('-id')
    permission_classes = [IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]  # 同时支持文件和表单数据

    def get(self, request, pk):
        """ 单个文章信息 """
        try:
            article = self.get_object()
        except Article.DoesNotExist:
            return Response(data={'error': '没有找到此文章'},
                            status=status.HTTP_404_NOT_FOUND)
        serializer = self.get_serializer(instance=article)
        return Response(data=serializer.data)

    def put(self, request, pk):
        """ 更新文章 """
        current_user = request.user
        try:
            instance = self.get_object()
        except Article.DoesNotExist:
            return Response(data={'error': '没有找到此文章'},
                            status=status.HTTP_404_NOT_FOUND)
        if instance.author != current_user:
            return Response(data={'error': '非创建者不能编辑文章'},
                            status=status.HTTP_403_FORBIDDEN)
        # 序列化并验证数据
        article_serializer = self.get_serializer(instance=instance,
                                                 data=request.data)
        article_serializer.is_valid(raise_exception=True)
        article_serializer.save()
        logger.info(f"{current_user} 编辑了文章： {instance.title}")
        return Response(data=article_serializer.data, status=status.HTTP_200_OK)

    def delete(self, request, pk):
        """删除文章"""
        current_user = request.user
        SA_MANAGER_ROLE = 'SA_Manager'
        ROLE_LIST = current_user.roles.all().values_list('role_name', flat=True)  # 因为是多对多对像，所以要取对象的属性
        # print(ROLE_LIST)
        # self.get_object() 根据请求的 URL 参数（如 pk 或 slug）从数据库中查询并返回单个模型实例
        # url 必须是 pk, 否则需要显示提定 lookup_url_kwarg
        # lookup_url_kwarg = 'id'  # 明确告诉 DRF 从 URL 的 'id' 参数中提取值
        try:
            instance = self.get_object()
        except Article.DoesNotExist:
            return Response(data={'error': '没有找到此文章！'},
                            status=status.HTTP_404_NOT_FOUND)
        if instance.author != current_user and SA_MANAGER_ROLE not in ROLE_LIST:
            return Response(data={'error': '只有创建者或SA_Manager可以删除'},
                            status=status.HTTP_403_FORBIDDEN)
        # 单独清理附件
        for attachment in instance.attachments.all():
            # attachment.file：指向 ArticleAttachment 模型中的 FileField 字段
            # file = models.FileField()   # 就是这里的 file 字段, 保存的是 MEDIA_ROOT 的物理位置
            # print(type(attachment))  # → <class 'ArticleAttachment'>
            # print(attachment.file)  # → "/attachments/test.pdf"
            # print(attachment.file.path)  # → 绝对物理路径
            # print(attachment.file.url)  # → 访问URL（如果有）
            attachment.file.delete()

        article_title = instance.title  # 先记录
        instance.delete()  # 触发 CASCADE 删除关联的 ArticleAttachment
        logger.info(f"{current_user} 删除了文章： {article_title}")
        return Response(status=status.HTTP_204_NO_CONTENT)


"""
文件上传的格式要求
浏览器通过 <form enctype="multipart/form-data"> 上传文件时，
数据会被编码为 multipart/form-data 格式（而非普通的 application/json）。
MultiPartParser 是 DRF 中专门解析这种格式的组件。

默认配置的局限性
DRF 默认的 parser_classes 是 [JSONParser, FormParser]，它们只能处理：
application/json（JSONParser）
application/x-www-form-urlencoded（FormParser）
但无法解析文件二进制数据。
"""


# 文件上传
class FileUploadAPIView(APIView):
    # 默认的parser_classes 是 [JSONParser, FormParser]
    # 指定这个视图只接受 multipart/form-data 格式的请求（文件上传的标准格式）
    parser_classes = [MultiPartParser]

    def post(self, request):
        upload_file = request.FILES.get('file')  # 假设前端表单字段是 `file`
        if not upload_file:
            return Response(
                data={'error': '未提供文件'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # 保存文件（默认使用模型upload_to规则存储）
        # default_storageDjango 提供的默认文件存储后端, 默认保存到 MEDIA_ROOT 设置指定的目录中
        # 语法：save(name, content, max_length=None)
        # name：文件保存的路径和名称
        # content：文件内容，通常是一个 UploadedFile 对象
        # max_length：可选，文件名的最大长度
        # file_path = default_storage.save()

        # 创建模型实例，让 FileField 自动处理存储 模型字段必需有upload_to属性
        # 直接将UploadedFile对象赋值给模型的FileField / ImageField
        # Django 会自动使用default_storage（通常是FileSystemStorage）保存文件
        # 文件存储路径由模型字段的upload_to参数决定
        instance = Article(attachment=upload_file)
        instance.save()

        return Response({
            'file_path': instance.attachment.name,  # 相对路径（如 'attachment/2024/06/15/filename.pdf'）
            'file_url': instance.attachment.url  # 完整 URL（需配置 MEDIA_URL）
        }, status=status.HTTP_201_CREATED)
